/*
 * NodoHoja.cpp
 *
 *  Created on: Sep 20, 2010
 *      Author: nicoabie
 */

#include "NodoHoja.h"

NodoHoja::NodoHoja() {
	this->altura = 0;
	this->id = 0;
	this->idHojaSiguiente = -1;
	this->cantidadRegistros = 0;
}

NodoHoja::NodoHoja(list<Registro*> registros, int IdHojaSiguiente) {
	this->registros = registros;
	this->idHojaSiguiente = IdHojaSiguiente;
}

list<Registro*>* NodoHoja::getListaRegistros() {
	Registro* registroAux;
	list<Registro*>* clonLista = new list<Registro*> ();

	list<Registro*>::iterator it = this->registros.begin();
	while (it != this->registros.end()) {
		registroAux = new Registro(((Registro*) (*it))->getKey(),
				((Registro*) (*it))->getDato());
		clonLista->push_back(registroAux);
		it++;
	}

	return clonLista;
}

void NodoHoja::setIdHojaSiguiente(int id) {
	this->idHojaSiguiente = id;
}

int NodoHoja::getIdHojaSiguiente() {
	return this->idHojaSiguiente;
}

int NodoHoja::insertarRegistro(Registro* registro) {

	if (getPosicionRegistro(registro) == -1) {
		registros.push_front(registro);
		registros.sort(&NodoHoja::compararRegistros);
		this->cantidadRegistros++;
	}
	//esta duplicado
	else
		return 1;

	return 0;
	//TODO ver cual es el tamanio maximo y ver si hubo overflow
}

bool NodoHoja::compararRegistros(Registro* registroUno, Registro* registroDos) {
	if ((registroUno->compararCon(registroDos)) > 0)
		return false;
	return true;
}

int NodoHoja::getPosicionRegistro(Registro* registro) {

	//TODO cambiar el for por un while, puristas.
	list<Registro*>::iterator it;
	int contadorPosiciones = 0;
	for (it = this->registros.begin(); it != this->registros.end(); it++) {
		if (registro->compararCon(*it) == 0)
			return contadorPosiciones;
		contadorPosiciones++;
	}

	return -1;

}

void NodoHoja::partir(Nodo* hojaHermana, long tamanioCubeta,
		char** keyAscendente, int* idNodoAscendente) {

	Nodo* hojaNueva = new NodoHoja();
	hojaNueva->setAltura(hojaHermana->getAltura());
	//TODO agregar id, debe hacerse consultando un archivo de con nùmero de cubetas libres

	list<Registro*>* listaCompletaDeRegistros = this->getListaRegistros();

	list<Registro*>* clonListaHermana =
			((NodoHoja*) hojaHermana)->getListaRegistros();

	list<Registro*>::iterator it = clonListaHermana->begin();

	while (it != clonListaHermana->end()) {
		listaCompletaDeRegistros->push_front(((Registro*) (*it)));
		it++;
	}
	listaCompletaDeRegistros->sort(&NodoHoja::compararRegistros);

	//Genero un iterador para llenar las tres cubetas
	list<Registro*>::iterator it2 = listaCompletaDeRegistros->begin();

	bool overflow = false;
	int contador=0;
	if (this->id == ((NodoHoja*) hojaHermana)->getIdHojaSiguiente()) {
		/*
		 * La hoja en la que estoy se encuentra a la derecha
		 * La hermana se encuentra a la izquierda
		 * La nueva hoja, al medio
		 */

		//actualizo los ids de hojas siguientes
		((NodoHoja*) hojaHermana)->setIdHojaSiguiente(hojaNueva->getId());
		((NodoHoja*) hojaNueva)->setIdHojaSiguiente(this->id);

		//borro la lista de registro de ambas hojas en las que
		//voy a rebalancear
		((NodoHoja*) hojaHermana)->borrarListaRegistros();
		this->borrarListaRegistros();

		do {
			if ((((Registro*) (*it2))->getTamanio()
					+ ((NodoHoja*) hojaHermana)->getEspacioOcupado())
					< tamanioCubeta && it2 != listaCompletaDeRegistros->end()) {
				hojaHermana->insertarRegistro(((Registro*) (*it2)));
				it2++;
				contador++;
			} else
				overflow = true;

		} while ((((NodoHoja*) hojaHermana)->getEspacioOcupado()
				< tamanioCubeta * 2 / 3) && !overflow);

		overflow = false;
		do {
			if ((((Registro*) (*it2))->getTamanio()
					+ ((NodoHoja*) hojaNueva)->getEspacioOcupado())
					< tamanioCubeta && it2 != listaCompletaDeRegistros->end()) {
				hojaNueva->insertarRegistro(((Registro*) (*it2)));
				it2++;
				contador++;
			} else
				overflow = true;

		} while ((((NodoHoja*) hojaNueva)->getEspacioOcupado() < tamanioCubeta
				* 2 / 3) && !overflow);

		overflow = false;
		do {
			if ((((Registro*) (*it2))->getTamanio() + this->getEspacioOcupado())
					< tamanioCubeta && it2 != listaCompletaDeRegistros->end()) {
				this->insertarRegistro(((Registro*) (*it2)));
				it2++;
				contador++;
			} else
				overflow = false;

		} while ((this->getEspacioOcupado() < tamanioCubeta * 2 / 3)
				&& !overflow);

		if (overflow || it2 != listaCompletaDeRegistros->end()) {
			//TODO lanzar una exception, se pudriò todo
			//no se pudo reorganizar la data en las tres cubetas
			cout<<listaCompletaDeRegistros->size()-contador;

		}

	} else {
		/*
		 * La hoja en la que estoy se encuentra a la izquierda
		 * La hermana se encuentra a la derecha
		 * La nueva hoja, al medio
		 */

		//actualizo los ids de hojas siguientes
		((NodoHoja*) hojaNueva)->setIdHojaSiguiente(hojaHermana->getId());
		this->setIdHojaSiguiente(hojaNueva->getId());

		//borro la lista de registro de ambas hojas en las que
		//voy a rebalancear
		((NodoHoja*) hojaHermana)->borrarListaRegistros();
		this->borrarListaRegistros();

		do {
			if ((((Registro*) (*it2))->getTamanio() + this->getEspacioOcupado())
					< tamanioCubeta && it2 != listaCompletaDeRegistros->end()) {
				this->insertarRegistro(((Registro*) (*it2)));
				it2++;
				contador++;
			} else
				overflow = false;

		} while ((this->getEspacioOcupado() < tamanioCubeta * 2 / 3)
				&& !overflow);

		overflow = false;
		do {
			if ((((Registro*) (*it2))->getTamanio()
					+ ((NodoHoja*) hojaNueva)->getEspacioOcupado())
					< tamanioCubeta && it2 != listaCompletaDeRegistros->end()) {
				hojaNueva->insertarRegistro(((Registro*) (*it2)));
				it2++;
				contador++;
			} else
				overflow = true;

		} while ((((NodoHoja*) hojaNueva)->getEspacioOcupado() < tamanioCubeta
				* 2 / 3) && !overflow);

		overflow = false;
		do {
			if ((((Registro*) (*it2))->getTamanio()
					+ ((NodoHoja*) hojaHermana)->getEspacioOcupado())
					< tamanioCubeta && it2 != listaCompletaDeRegistros->end()) {
				hojaHermana->insertarRegistro(((Registro*) (*it2)));
				it2++;
				contador++;
			} else
				overflow = true;

		} while ((((NodoHoja*) hojaHermana)->getEspacioOcupado()
				< tamanioCubeta * 2 / 3) && !overflow);

		if (overflow || it2 != listaCompletaDeRegistros->end()) {
			//TODO lanzar una exception, se pudriò todo
			//no se pudo reorganizar la data en las tres cubetas
			cout<<listaCompletaDeRegistros->size()-contador;

		}
	}

	//TODO persistir la hoja nueva
	//	idNodoAscendente = &(hojaNueva->getId());

}

long NodoHoja::getEspacioOcupado() {

	long espacioOcupado = 0;

	//ALTURA
	espacioOcupado += CANTIDAD_BYTES_TAMANIOS_ATRIBUTOS_PRIVADOS_NODO;
	//CANTIDAD DE REGISTROS
	espacioOcupado += CANTIDAD_BYTES_TAMANIOS_ATRIBUTOS_PRIVADOS_NODO;
	//ID
	espacioOcupado += CANTIDAD_BYTES_TAMANIOS_ATRIBUTOS_PRIVADOS_NODO;
	//ID HOJA SIGUIENTE
	espacioOcupado += CANTIDAD_BYTES_TAMANIOS_ATRIBUTOS_PRIVADOS_NODO;

	list<Registro*>::iterator it = this->registros.begin();
	while (it != this->registros.end()) {
		espacioOcupado += ((Registro*) (*it))->getTamanio();
		it++;
	}

	return espacioOcupado;
}

string NodoHoja::toString() {

	string stream="";

	Registro* registro;

	//stream +="L";


	stream += Utils::IntToString(this->id,CANTIDAD_BYTES_TAMANIOS_ATRIBUTOS_PRIVADOS_NODO);
	stream += Utils::IntToString(this->altura,CANTIDAD_BYTES_TAMANIOS_ATRIBUTOS_PRIVADOS_NODO);
	stream += Utils::IntToString(this->cantidadRegistros,CANTIDAD_BYTES_TAMANIOS_ATRIBUTOS_PRIVADOS_NODO);

	list<Registro*>::iterator it;
		for (it = this->registros.begin(); it != this->registros.end(); it++) {
			registro = (Registro*) *it;
			stream+=registro->toString();
		}

	stream += Utils::IntToString(this->idHojaSiguiente,CANTIDAD_BYTES_TAMANIOS_ATRIBUTOS_PRIVADOS_NODO);

	return stream;

	/*char* cAux = (char*) malloc(0);

	//Se reasigna el tamanio de cAux para grabar el id
	cAux = (char*) realloc(cAux, sizeof(int));
	memcpy(cAux, (char*) &(this->id), sizeof(int));

	ios->write(cAux, sizeof(int));

	//Se graba la altura
	memcpy(cAux, (char*) &(this->altura), sizeof(int));

	ios->write(cAux, sizeof(int));

	//Se graba la cantidad de registros
	memcpy(cAux, (char*) &(this->cantidadRegistros), sizeof(int));

	ios->write(cAux, sizeof(int));

	list<Registro*>::iterator it;
	for (it = this->registros.begin(); it != this->registros.end(); it++) {
		registro = (Registro*) *it;
		registro->persistir(ios);
	}

	//Se graba el id de la hoja siguiente
	memcpy(cAux, (char*) &(this->idHojaSiguiente), sizeof(int));

	ios->write(cAux, sizeof(int));*/
}

void NodoHoja::fromString(string stream) {

	/*
	 * Se hidrata del archivo donde su orden es:
	 * <id><atura><cantidadRegistros><registros><idNodoSiguiente>
	 */

	this->id = Utils::StringToInt(stream.substr(0,CANTIDAD_BYTES_TAMANIOS_ATRIBUTOS_PRIVADOS_NODO));
	this->altura = Utils::StringToInt(stream.substr(4,CANTIDAD_BYTES_TAMANIOS_ATRIBUTOS_PRIVADOS_NODO));
	int cantidadRegistrosAux = Utils::StringToInt(stream.substr(8,CANTIDAD_BYTES_TAMANIOS_ATRIBUTOS_PRIVADOS_NODO));

	stream = stream.substr(12,stream.length());

	//registros
	for (int i = 0; i < cantidadRegistrosAux; i++) {
		Registro* registro = new Registro(&stream);
		this->insertarRegistro(registro);
	}

	this->idHojaSiguiente = Utils::StringToInt(stream);

	/*
	char* cAux = (char*) malloc(0);//se inicializa cAux

	//id
	cAux = (char*) realloc(cAux, sizeof(ios));
	ios->read(cAux, sizeof(int));

	this->id = 0;
	this->id = (cAux[3] << 24) | (cAux[2] << 16) | (cAux[1] << 8) | (cAux[0]
			<< 0) | this->id;

	//altura
	ios->read(cAux, sizeof(int));
	this->altura = 0;
	this->altura = (cAux[3] << 24) | (cAux[2] << 16) | (cAux[1] << 8)
			| (cAux[0] << 0) | this->altura;

	//cantidad de registros
	ios->read(cAux, sizeof(int));
	int cantidadAux = 0;
	cantidadAux = (cAux[3] << 24) | (cAux[2] << 16) | (cAux[1] << 8) | (cAux[0]
			<< 0) | cantidadAux;

	//registros
	for (int i = 0; i < cantidadAux; i++) {
		Registro* registro = new Registro(stream);
		this->insertarRegistro(registro);
	}
	this->setCantidadRegistros(cantidadAux);

	//id nodo siguiente
	ios->read(cAux, sizeof(int));
	this->idHojaSiguiente = 0;
	this->idHojaSiguiente = (cAux[3] << 24) | (cAux[2] << 16) | (cAux[1] << 8)
			| (cAux[0] << 0) | this->idHojaSiguiente;

	*/

}

void NodoHoja::borrarListaRegistros() {
	list<Registro*>::iterator it = this->registros.begin();
	while (it != this->registros.end()) {
		delete (Registro*) *it;
		it++;
	}

	this->registros.clear();

	this->cantidadRegistros = 0;
}

NodoHoja::~NodoHoja() {

	this->borrarListaRegistros();

}
